package msg.xml;

import java.io.StringWriter;
import java.util.Collection;

import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;

import msg.Message;
import msg.visitors.MessageToXML;

import org.w3c.dom.*;

import common.Configuration;

/**
 * Transforms a collection of messages into a valid XML under the DTD
 * 
 * @author Gal Lalouche
 */
public class XMLTransformer {
	
	private static final String	DTD			= "Messages.dtd";
	private static final String	KML			= "toKML.xsl";
	private static final String	MESSAGES	= "messagesOnly.xsl";
	private static final String	EVENTS		= "eventsOnly.xsl";
	private static final String	KML_PATH	= "toKMLPath.xsl";
	
	private static String translatePath(String path) {
		return Configuration.getInstance().getRealPath() + "/" + path;
	}
	
	private static Node transform(Node node, String xsltName) throws TransformerConfigurationException,
			TransformerFactoryConfigurationError, TransformerException {
		Transformer transformer = TransformerFactory.newInstance().newTransformer(new StreamSource(xsltName));
		DOMResult result = new DOMResult();
		transformer.transform(new DOMSource(node), result);
		Node resultNode = result.getNode();
		return resultNode;
	}
	
	/**
	 * Creates an XML document valid under the DTD
	 * 
	 * @param messages The messages to parse
	 * @return An XML Document
	 */
	public static Document toXML(Collection<? extends Message> messages) {
		try {
			DOMImplementation builder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
					.getDOMImplementation();
			DocumentType type = builder.createDocumentType("messages", null, translatePath(DTD));
			Document doc = builder.createDocument(null, "MESSAGES", type);
			// DocumentFragment root = doc.createDocumentFragment();
			MessageToXML msgToXMLer = new MessageToXML(doc);
			Node root = doc.getChildNodes().item(1);
			for (Message msg: messages) {
				try {
					root.appendChild(msg.accept(msgToXMLer));
				} catch (Exception e) {
					throw new AssertionError(e);
				}
			}
			
			return doc;
		} catch (ParserConfigurationException e) {
			throw new AssertionError(e);
		}
	}
	
	/**
	 * @param node The existing document to parse
	 * @return An XML of only the messages
	 */
	public static Node toMessagesOnly(Node node) {
		try {
			return transform(node, translatePath(MESSAGES));
		} catch (Exception e) {
			throw new AssertionError(e);
		}
	}
	
	/**
	 * @param node The existing document to parse
	 * @return An XML of only the events
	 */
	public static Node toEventsOnly(Node node) {
		try {
			return transform(node, translatePath(EVENTS));
		} catch (Exception e) {
			throw new AssertionError(e);
		}
	}
	
	/**
	 * Creates a KML XML document of the XML document
	 * 
	 * @param node The existing document to parse
	 * @return An KML document
	 */
	public static Node toKML(Node node) {
		try {
			return transform(node, translatePath(KML));
		} catch (Exception e) {
			throw new AssertionError(e);
		}
	}
	
	/**
	 * Creates a KML XML document of the XML document
	 * 
	 * @param node The existing document to parse
	 * @return An KML document
	 */
	public static Node toKMLPath(Node node) {
		try {
			return transform(node, translatePath(KML_PATH));
		} catch (Exception e) {
			throw new AssertionError(e);
		}
	}
	
	/**
	 * Turns an XML node to a string
	 * 
	 * @param element The XML element
	 * @return A string of the element
	 */
	public static String elementToString(Node element) {
		try {
			TransformerFactory transFactory = TransformerFactory.newInstance();
			Transformer transformer = transFactory.newTransformer();
			StringWriter buffer = new StringWriter();
			transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
			transformer.transform(new DOMSource(element), new StreamResult(buffer));
			String str = buffer.toString();
			return str;
		} catch (Exception e) {
			throw new AssertionError(e);
		}
	}
}
